﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

namespace ArgusLib.Controls
{
	public class FolderSelectDialog : CommonDialog
	{
		static bool IsNewDialogSupported
		{
			get { return (ArgusLib.Environment.Runtime == Runtime.CLR && System.Environment.OSVersion.Platform == PlatformID.Win32NT && System.Environment.OSVersion.Version.Major >= 6); }
		}

		OpenFileDialog openFileDialog;
		FolderBrowserDialog folderBrowserDialog;

		public FolderSelectDialog()
		{
			this.openFileDialog = new OpenFileDialog();
			this.openFileDialog.AddExtension = false;
			this.openFileDialog.CheckFileExists = false;
			this.openFileDialog.DereferenceLinks = true;
			this.openFileDialog.Multiselect = false;
		}

		public string InitialDirectory
		{
			get { return this.openFileDialog.InitialDirectory; }
			set { this.openFileDialog.InitialDirectory = value; }
		}

		public string Title
		{
			get { return this.openFileDialog.Title; }
			set { this.openFileDialog.Title = value; }
		}

		public bool Multiselect
		{
			get { return this.openFileDialog.Multiselect; }
			set { this.openFileDialog.Multiselect = value; }
		}

		public string SelectedPath
		{
			get { return this.openFileDialog.FileName; }
		}

		public string[] SelectedPaths
		{
			get { return this.openFileDialog.FileNames; }
		}

		protected override bool RunDialog(IntPtr hwndOwner)
		{
			if (FolderSelectDialog.IsNewDialogSupported == true)
			{
				return FolderSelectDialog.ShowVistaDialog(this.openFileDialog, hwndOwner);
			}
			else
			{
				if (this.folderBrowserDialog == null)
					this.folderBrowserDialog = new FolderBrowserDialog();

				FolderSelectDialog.UpdateFolderBrowserDialog(this.folderBrowserDialog, this.openFileDialog);
				if (this.folderBrowserDialog.ShowDialog(new Win32Window(hwndOwner)) == DialogResult.OK)
				{
					this.openFileDialog.FileName = this.folderBrowserDialog.SelectedPath;
					return true;
				}
				else
				{
					return false;
				}
			}
		}

		public override void Reset()
		{
			throw new NotImplementedException();
		}

		private static void UpdateFolderBrowserDialog(FolderBrowserDialog fbd, OpenFileDialog ofd)
		{
			fbd.Description = ofd.Title;
			fbd.SelectedPath = ofd.InitialDirectory;
		}

		private static bool ShowVistaDialog(OpenFileDialog openFileDialog, IntPtr hwndOwner)
		{
			MethodInfo mi = typeof(OpenFileDialog).GetMethod("CreateVistaDialog", BindingFlags.Instance | BindingFlags.NonPublic);
			object dialog = mi.Invoke(openFileDialog, null);
			mi = typeof(OpenFileDialog).GetMethod("OnBeforeVistaDialog", BindingFlags.Instance | BindingFlags.NonPublic);
			mi.Invoke(openFileDialog, new object[] { dialog });

			mi = typeof(FileDialog).GetMethod("GetOptions", BindingFlags.Instance | BindingFlags.NonPublic);
			FOS options = (FOS)mi.Invoke(openFileDialog, null);
			options |= FOS.FOS_PICKFOLDERS;

			Assembly assembly = Assembly.GetAssembly(typeof(FileDialog));
			Type tIFileDialog = assembly.GetType("System.Windows.Forms.FileDialogNative+IFileDialog");
			mi = tIFileDialog.GetMethod("SetOptions", BindingFlags.Instance | BindingFlags.Public);
			mi.Invoke(dialog, new object[] { (uint)options });

			Type tVistaDialogEvents = typeof(FileDialog).GetNestedType("VistaDialogEvents", BindingFlags.NonPublic);
			object vistaDialogEvents = Activator.CreateInstance(tVistaDialogEvents, openFileDialog);
			object[] eventParameter = new object[] { vistaDialogEvents, 0u };

			mi = tIFileDialog.GetMethod("Advise", BindingFlags.Instance | BindingFlags.Public);
			mi.Invoke(dialog, eventParameter);

			mi = tIFileDialog.GetMethod("Show", BindingFlags.Instance | BindingFlags.Public);

			bool RetVal = false;

			try
			{
				if ((int)mi.Invoke(dialog, new object[] { hwndOwner }) == 0)
					RetVal = true;
			}
			finally
			{
				mi = tIFileDialog.GetMethod("Unadvise", BindingFlags.Instance | BindingFlags.Public);
				mi.Invoke(dialog, new object[] { eventParameter[1] });
			}
			return RetVal;
		}

		[Flags]
		enum FOS : uint
		{
			FOS_OVERWRITEPROMPT = 2,
			FOS_STRICTFILETYPES = 4,
			FOS_NOCHANGEDIR = 8,
			FOS_PICKFOLDERS = 32,
			FOS_FORCEFILESYSTEM = 64,
			FOS_ALLNONSTORAGEITEMS = 128,
			FOS_NOVALIDATE = 256,
			FOS_ALLOWMULTISELECT = 512,
			FOS_PATHMUSTEXIST = 2048,
			FOS_FILEMUSTEXIST = 4096,
			FOS_CREATEPROMPT = 8192,
			FOS_SHAREAWARE = 16384,
			FOS_NOREADONLYRETURN = 32768,
			FOS_NOTESTFILECREATE = 65536,
			FOS_HIDEMRUPLACES = 131072,
			FOS_HIDEPINNEDPLACES = 262144,
			FOS_NODEREFERENCELINKS = 1048576,
			FOS_DONTADDTORECENT = 33554432,
			FOS_FORCESHOWHIDDEN = 268435456,
			FOS_DEFAULTNOMINIMODE = 536870912
		}

		class Win32Window : IWin32Window
		{
			IntPtr hwnd;

			public Win32Window(IntPtr hwnd)
			{
				this.hwnd = hwnd;
			}

			public IntPtr Handle { get { return this.hwnd; } }
		}
	}
}
